#include "CreaseStructure.hpp"
#include "MeshAlgorithm.hpp"
#include "DebugHelper.hpp"
using namespace MeshAlgorithm;

CreaseStructure::CreaseStructure()
{

}
CreaseStructure::~CreaseStructure()
{
   Clear();
}
void VertexLoop::Normalize()
{
   IndexList::iterator iter = std::min_element(mVertices.begin(), mVertices.end());
   int index = (iter - mVertices.begin());
   int nextIndex = (index + 1)%mVertices.size();
   int prevIndex = (index + mVertices.size() + 1)%mVertices.size();
   if (mVertices[prevIndex] < mVertices[nextIndex])
   {
      std::reverse(mVertices.begin(), mVertices.end());
      iter = std::min_element(mVertices.begin(), mVertices.end());
   }
   if (iter != mVertices.begin())
   {
      int vcount = mVertices.size();
      std::rotate(mVertices.begin(), iter, mVertices.end());
   }
}
namespace 
{

struct RegionInfo
{
   RegionInterface mRegion;
   int             mFaceCount;
   std::vector<int>  mVisitedFaces;
   EdgeList        mEdges;
};

void FindCreaseRegions(const FaceList& aFaces,
                       CreaseStructure& aCreases,
                       std::set<int>&   unvisitedFaces)
{
   DEBUG_LOG("FindCreaseRegions")
   // Collect region info in this temporary storage
   std::vector<RegionInfo> regionInfos;

   // build adjacency map
   AdjacencyMap edges;
   const std::vector<Face>& faces = aFaces;
   MakeAdjacencyMap(faces, edges);
   
   while (! unvisitedFaces.empty())
   {
      std::vector<Edge> containedEdges;
      //std::set<int> visitedFaces;
      //std::vector<Edge> nextEdges;
      std::vector<int> nextFaces;
      std::vector<int> interiorFaces;
      int fi = *unvisitedFaces.begin();
      const Face& rootFace = faces[fi];
      RegionInterface interface(rootFace.materials[0], rootFace.materials[1]);
      OrderEdge(interface);
      nextFaces.push_back(fi);
      while (! nextFaces.empty())
      {
         int faceIndex = nextFaces.back();
         nextFaces.pop_back();
         if (unvisitedFaces.find(faceIndex) == unvisitedFaces.end()) { continue; }
         interiorFaces.push_back(faceIndex);
         unvisitedFaces.erase(faceIndex);
         //visitedFaces.insert(faceIndex);
         const Face& f = faces[faceIndex];
         Edge e[] = { Edge(f.vertex[0], f.vertex[1]), Edge(f.vertex[1], f.vertex[2]), Edge(f.vertex[2], f.vertex[0]) };
         for (int j = 0; j < 3; ++j)
         {
            OrderEdge(e[j]);
            containedEdges.push_back(e[j]);
            AdjacentFaces& a_faces = edges[e[j]];
            for (size_t k = 0; k < a_faces.size(); ++k)
            {
               int ind = a_faces[k];
               const Face& nextFace = faces[ind];
               if (ind != faceIndex && region_equal(RegionInterface(nextFace.materials[0], nextFace.materials[1]), interface))
               {
                  nextFaces.push_back(ind);
               }
            }
         }
      }
      std::vector<Edge> creases;
      std::sort(containedEdges.begin(), containedEdges.end());
      size_t i(1);
      for (; i < containedEdges.size();)
      {
         if (containedEdges[i-1] != containedEdges[i])
         {
            creases.push_back(containedEdges[i-1]);
            ++i;
         }
         else
         {
            while (i < containedEdges.size() && containedEdges[i] == containedEdges[i-1])
            {
               ++i;
            }
            ++i;
         }
      }
      if (i == containedEdges.size())
      {
         creases.push_back(containedEdges[i-1]);
      }
      RegionInfo info;
      info.mEdges = creases;
      info.mRegion = interface;
      info.mFaceCount = interiorFaces.size();
      std::swap(info.mVisitedFaces, interiorFaces);
      regionInfos.push_back(info);
   }

   // Now move from temporary storage to final structure
   for (size_t i = 0; i < regionInfos.size(); ++i)
   {
      RegionInfo& info = regionInfos[i];
      RegionCrease* crease = new RegionCrease();
      aCreases.mPatches.push_back(crease);
      crease->mInterface = info.mRegion;
      std::swap(crease->mFaces, info.mVisitedFaces);

      MeshAlgorithm::EdgeCycleList cycles;
      MeshAlgorithm::FindCycles(info.mEdges, 1000000, cycles);
      if (cycles.size() > 0)
      {
         //crease.mLoops.resize(cycles.size());
         for (EdgeCycleList::iterator iter = cycles.begin(); iter != cycles.end(); ++iter)
         {
            VertexLoop* loop = new VertexLoop;
            EdgeCycle& cycle = *iter;
            std::swap(loop->mVertices, cycle);
            loop->Normalize();
            loop->RecomputeHash();
            loop = aCreases.Add(loop);
            crease->mLoops.push_back(loop);
            loop->mCreases.push_back(crease);
         }
      }
      else
      {
         aCreases.mPatches.pop_back();
      }
   }
   DEBUG_LOG(".\n")
}

void FindCreaseRegions(const FaceList& aFaces,
                       CreaseStructure& aCreases)
{
   std::set<int> unvisitedFaces;
   unvisitedFaces.insert(boost::counting_iterator<int>(0), boost::counting_iterator<int>(aFaces.size()));
   FindCreaseRegions(aFaces, aCreases, unvisitedFaces);
}

}

void CreaseStructure::Build(const FaceList& aFaces)
{
   Clear();
   std::list<RegionCrease> creases;
   FindCreaseRegions(aFaces, *this);
}

void CreaseStructure::Rebuild(const FaceList&   aFaces,
                              std::vector<int>& aScrapRegions,
                              std::vector<int>& aScrapLoops)
{
   std::sort(aScrapRegions.begin(), aScrapRegions.end(), std::greater<int>());   
   aScrapRegions.erase(std::unique(aScrapRegions.begin(), aScrapRegions.end()), aScrapRegions.end());
   std::sort(aScrapLoops.begin(), aScrapLoops.end(), std::greater<int>());
   aScrapLoops.erase(std::unique(aScrapLoops.begin(), aScrapLoops.end()), aScrapLoops.end());
   for (size_t i = 0; i < aScrapRegions.size(); ++i)
   {
      RegionCrease* patch = mPatches[aScrapRegions[i]];
      for (size_t j = 0; j < patch->mLoops.size(); ++j)
      {
         patch->mLoops[j]->RemoveRegion(patch);
      }
      mPatches.erase(mPatches.begin() + aScrapRegions[i]);
      delete patch;
   }

   for (size_t i = 0; i < aScrapLoops.size(); ++i)
   {
      VertexLoop* loop = mLoops[aScrapLoops[i]];
      if (loop->mCreases.empty())
      {
         mLoops.erase(mLoops.begin() + aScrapLoops[i]);
         delete loop;
      }
      // loop->mCreases[j]->RemoveLoop(loop);
   }
   std::vector<int> visitedFaces;
   for (size_t i = 0; i < mPatches.size(); ++i)
   {
      visitedFaces.insert(visitedFaces.end(), mPatches[i]->mFaces.begin(), mPatches[i]->mFaces.end());
   }
   std::vector<int> unvisitedFaces;
   
   std::sort(visitedFaces.begin(), visitedFaces.end());
   std::set_difference(boost::counting_iterator<int>(0), boost::counting_iterator<int>(aFaces.size()),
                           visitedFaces.begin(), visitedFaces.end(), std::back_inserter(unvisitedFaces));
   
   FindCreaseRegions(aFaces, *this, std::set<int>(unvisitedFaces.begin(), unvisitedFaces.end()));
}
